package com.youku.mson.lexer;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Lexer {

	private String json;

	private State state = State.START;

	private int pos = 0;

	private int mark = 0;

	private List<Token> tokens = new LinkedList<>();

	private static final Map<String, TokenType> keywords;

	private static final String[] keyword_array = { "true", "false", "null" };

	static {
		keywords = new HashMap<>();

		keywords.put("true", TokenType.TRUE);
		keywords.put("false", TokenType.FALSE);
		keywords.put("null", TokenType.NULL);
	}

	public Lexer(String json) {
		this.json = json + '\0';

	}

	public void parse() {
		while (pos < json.length()) {
			char ch = json.charAt(pos);
			switch (state) {
			case START:
				handleStart(ch);
				break;
			case KEYWORD:
				handleKeyword(ch);
				break;
			case STRING:
				handleString(ch);
				break;
			case NUMBER:
				handleNumber(ch);
				break;
			default:
				throw new RuntimeException("不存在该状态" + state);
			}

			pos++;
		}
	}

	private void handleNumber(char ch) {
		if (ch == '.') {

		} else if (isNumber(ch)) {

		} else {
			addToken(mark, pos, TokenType.NUMBER);
			state = State.START;
			pos--;
		}
	}

	private void handleKeyword(char ch) {
		if (isLetter(ch) || isNumber(ch)) {

		} else {
			String keyword = json.substring(mark, pos);
			TokenType type = keywords.get(keyword);
			if (type != null) {
				addToken(mark, pos, type);
				state = State.START;
				pos--;
			} else {
				throw new RuntimeException("无法识别" + Arrays.toString(keyword_array) + "以外的关键字");
			}
		}
	}

	private void handleString(char ch) {
		if (ch == '"') {
			addToken(mark + 1, pos, TokenType.STRING);
			state = State.START;
		}
	}

	private void handleStart(char ch) {
		switch (ch) {
		case '\0':
			break;
		case '{':
			addToken(1, TokenType.LBRACE);
			break;
		case '}':
			addToken(1, TokenType.RBRACE);
			break;
		case '[':
			addToken(1, TokenType.LBRACKET);
			break;
		case ']':
			addToken(1, TokenType.RBRACKET);
			break;
		case ':':
			addToken(1, TokenType.COLON);
			break;
		case ',':
			addToken(1, TokenType.COMMA);
			break;
		case '"':
			mark = pos;
			state = State.STRING;
			break;
		default:
			if (isLetter(ch)) {
				mark = pos;
				state = State.KEYWORD;
			} else if (isNumber(ch)) {
				mark = pos;
				state = State.NUMBER;
			} else {
				throw new RuntimeException("无法识别索引位置在" + pos + "的字符" + ch);
			}
			break;
		}
	}

	private boolean isNumber(char ch) {
		return ch >= '0' && ch <= '9';
	}

	private boolean isLetter(char ch) {
		return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
	}

	private void addToken(int len, TokenType type) {
		tokens.add(new Token(json.substring(pos, pos + len), type));
	}

	private void addToken(int start, int end, TokenType type) {
		tokens.add(new Token(json.substring(start, end), type));
	}

	public List<Token> tokens() {
		return tokens;
	}
}
